package user

import (
	"bufio"
	"fmt"
	"github.com/gogf/gf/os/glog"
	"github.com/pkg/errors"
	"gitlab.local/DO-module/new-filemanage-module/src/core/quotactl"
	"gitlab.local/DO-module/new-filemanage-module/src/core/redisdb"
	"gitlab.local/TerraMaster/tos-modules/database/sqlitemodel"
	"gitlab.local/TerraMaster/tos-modules/domain/domain_commom"
	userLib "gitlab.local/golibrary/user"
	"gorm.io/gorm"
	"io"
	"os"
	"os/user"
	"sort"
	"strconv"
	"strings"
	"time"
)

const (
	SysPass   = "/etc/passwd"
	SysShadow = "/etc/shadow"
	SysGroup  = "/etc/group"
	SysSamba  = "/etc/samba/smbp"
)

func (u *User) SysUserList(start, limit int) (result []UserInfo, err error) {
	var userList []*sqlitemodel.UserTable
	db := u.liteDb.DbGet().Table("user_table")
	tx := db.Where("password in('system')")
	if limit > 0 {
		tx.Limit(limit).Offset(start)
	}
	if err = tx.Find(&userList).Error; err != nil {
		glog.Error("UserList", err)
		return
	}
	return u.getUserInfo(userList)
}

// UserList 获取用户列表
func (u *User) UserList(start, limit int) (result []UserInfo, err error) {
	var userList []*sqlitemodel.UserTable
	db := u.liteDb.DbGet().Table("user_table")
	tx := db.Where("password not in('group','adnet','adgroup', 'system')")
	if limit > 0 {
		tx.Limit(limit).Offset(start)
	}
	if err = tx.Find(&userList).Error; err != nil {
		glog.Error("UserList", err)
		return
	}
	return u.getUserInfo(userList)
}

func (u *User) getUserInfo(userList []*sqlitemodel.UserTable) (result []UserInfo, err error) {
	result = make([]UserInfo, 0)
	for _, one := range userList {
		userInfo := UserInfo{
			Name:        one.Username,
			Email:       one.Email,
			Phone:       one.Phone,
			CloudActive: one.CloudActive,
			Description: one.Remarks,
			Alias:       one.Alias,
			IsNvr:       one.IsNvr,
		}
		lookup, err := user.Lookup(userInfo.Name)
		if err != nil {
			glog.Error("UserList1", err)
			continue
		}
		// 返回是否为超级管理员
		userInfo.IsFirstAdmin = u.isFirstAdmin(userInfo.Name)
		userInfo.Disabled = u.isSystemUser(userInfo.Name)
		if userInfo.Name == "admin" && !userInfo.IsFirstAdmin {
			continue
		}
		// 获取用户id
		userInfo.Uid = lookup.Uid
		quotaList := quotactl.New().Get(userInfo.Name)
		for _, item := range quotaList {
			if item.Limit == 0 {
				continue
			}
			userInfo.QuotaSize = true
		}
		expire := u.ExpireDays(userInfo.Name)
		if expire == 1 {
			userInfo.State = 0 // disable
		} else if expire == 0 {
			userInfo.State = 1 // normal
		} else {
			if time.Now().Unix()-expire < 0 {
				userInfo.State = 1 // normal
			} else {
				userInfo.State = 2 // expired
			}
		}
		userInfo.GroupList = u.aclGetter.GetUserInGroups(userInfo.Name)
		result = append(result, userInfo)
	}
	sort.Sort(UserSlice(result))
	return
}

func (u *User) isFirstAdmin(name string) bool {
	return os.Getenv("admin") == name
}

func (u *User) isSystemUser(name string) bool {
	if u.isFirstAdmin(name) || name == "guest" {
		return true
	}
	return false
}

type DataService struct {
	store *redisdb.RedisDb
}

func (u *User) ParseFile(path string, handleFunc func(reader *bufio.Reader) error) error {
	f, err := os.Open(path)
	if err != nil {
		return errors.Wrap(err, fmt.Sprintf("open %s faild", path))
	}
	defer f.Close()
	return handleFunc(bufio.NewReader(f))
}

// ExpireDays 帐号过期时间策略：
// 0 -> 不停用此帐号
// 1 -> 立即停用
// (>1) -> 按日期停用
// db 数据库连接，域用户必须传
func (u *User) ExpireDays(name string) (result int64) {
	db := u.liteDb.DbGet()
	if DomainUserExpireDays(name, db) == 1 {
		return 1
	}

	_ = u.ParseFile(SysShadow, func(reader *bufio.Reader) error {
		for {
			b, _, err := reader.ReadLine()
			if err == io.EOF {
				break
			}
			line := string(b)
			if !strings.HasPrefix(line, fmt.Sprintf("%s:", name)) {
				continue
			}
			fp := strings.Split(line, ":")
			expireString := strings.TrimSpace(fp[7])
			if expireString != "" {
				expire, err := strconv.ParseInt(expireString, 10, 64)
				if err != nil {
					break
				}
				result = expire
			}
			break
		}
		return nil
	})
	if result > 1 {
		result = CalculateExpire(result)
	}
	return
}

// domainUserExpireDays 域用户禁用限制
// 0 -> 不停用此帐号
// 1 -> 立即停用
func DomainUserExpireDays(name string, db *gorm.DB) int64 {
	if db == nil {
		return 1
	}
	var expire int64
	tx := db.Table("user_table").Where("username=?", name).Select("status").Scan(&expire)
	if tx.Error != nil {
		fmt.Printf("domainUserExpireDays fail, err: " + tx.Error.Error())
	}

	t, _, err := domain_commom.DomainConf()
	if err != nil {
		fmt.Println(err)
		return 1
	}

	var status string
	switch t {
	case 1:
		status = ParseAdDomainUserStatus(expire)
	case 2:
		status = ParseLdapDomainUserStatus(expire)
	}

	switch status {
	case "1", "2":
		return 1
	default:
		return 0
	}
}

// "0": 正常;  "1": 停用; "2": 已过期; "string": 到期于string
func ParseAdDomainUserStatus(expire int64) string {
	switch {
	case expire == 9223372036854775807 || expire == 0:
		return "0"
	case expire == 1:
		return "1"
	default:
		seconds := (expire - 116444736000000000) / 10000000
		startTime := time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC)
		expirationTime := startTime.Add(time.Duration(seconds) * time.Second)
		if time.Now().Sub(expirationTime).Nanoseconds() >= 0 {
			return "2"
		} else {
			return expirationTime.Format("2006-01-02")
		}
	}
}

// "0": 正常;  "1": 停用; "2": 已过期; "string": 到期于string
func ParseLdapDomainUserStatus(expire int64) string {
	orin := time.Date(1970, time.January, 1, 0, 0, 0, 0, time.UTC)
	switch {
	case expire < 1:
		return "0"
	case expire == 1:
		return "1"
	case expire > 1 && expire <= int64(time.Now().Sub(orin).Hours())/24:
		return "2"
	default:
		return orin.AddDate(0, 0, int(expire)).Format("2006-01-02")
	}
}

func CalculateExpire(day int64) int64 {
	addDay := time.Duration(day) * 24 * time.Hour
	lo := time.Date(1970, 01, 01, 0, 0, 0, 0, time.Local).Add(addDay)
	return lo.Unix()
}

// GetUsersByGroup 获取在这个组里的用户
func (u *User) GetUsersByGroup(group string) []string {
	result := make([]string, 0)
	if !strings.HasPrefix(group, "@") {
		group = fmt.Sprintf("@%s", group)
	}
	guList := make([]sqlitemodel.GroupUsers, 0)
	err := u.liteDb.DbGet().Model(&sqlitemodel.GroupUsers{}).Where("groupname=?", group).Find(&guList).Error
	if err != nil {
		return result
	}
	for _, item := range guList {
		result = append(result, item.UserName)
	}
	return result
}

// GetUserInGroups 获取用户所在的用户组
func (u *User) GetUserInGroups(username string) []string {
	result := make([]string, 0)
	ul, err := userLib.Lookup(username)
	if err != nil {
		return result
	}
	gids, err := ul.GroupIds()
	if err != nil {
		return result
	}
	for _, gid := range gids {
		g, err := userLib.LookupGroupId(gid)
		if err != nil {
			continue
		}
		result = append(result, g.Name)
	}
	return result
}

// GetUserOrGroup 获取所有用户组及用户名称 [用户组][]用户名称
func (u *User) GetUserOrGroup() (map[string][]string, error) {
	var groupData, userData []sqlitemodel.UserTable
	db := u.liteDb.DbGet()
	_ = db.Where("password=?", "group").Select("username").Find(&groupData)
	_ = db.Where("password not in ?", []string{"group", "adnet", "adgroup"}).Select("username").Find(&userData)
	userOrGroup := make(map[string][]string)
	for _, v := range groupData {
		gName := strings.TrimPrefix(v.Username, "@")
		_, err := userLib.LookupGroup(gName)
		if err != nil {
			continue
		}
		userOrGroup[gName] = make([]string, 0)
	}
	for _, v := range userData {
		//如果为admin,不为超级管理员则跳过
		if v.Username == "admin" {
			continue
		}
		_, err := userLib.Lookup(v.Username)
		if err != nil {
			continue
		}
		for _, val := range u.UserGroups(v.Username) {
			if _, b := userOrGroup[val]; b {
				userOrGroup[val] = append(userOrGroup[val], v.Username)
			}
		}
	}
	return userOrGroup, nil
}

// UserGroups 获取某个用户的全部所属用户组名称
func (u *User) UserGroups(uname string) []string {
	groups := make([]string, 0)
	lookup, err := userLib.Lookup(uname)
	if err != nil {
		return groups
	}
	ids, err := lookup.GroupIds()
	if err != nil {
		return groups
	}
	for _, v := range ids {
		id, err := userLib.LookupGroupId(v)
		if err != nil {
			continue
		}
		groups = append(groups, id.Name)
	}
	return groups
}
